/*
 * Copyright (c) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


import { ts, ClassSignature, MethodSignature, ArkMethod, ArkAssignStmt, AbstractBinopExpr, ArkFile, Stmt } from 'arkanalyzer/lib';
import Logger, { LOG_MODULE_TYPE } from 'arkanalyzer/lib/utils/logger';
import { BaseChecker, BaseMetaData } from '../BaseChecker';
import { Defects } from '../../model/Defects';
import { ClassMatcher, FileMatcher, MatcherCallback, MatcherTypes, MethodMatcher } from '../../matcher/Matchers';
import { Rule } from '../../model/Rule';
import { RuleListUtil } from '../../utils/common/DefectsList';
import { IssueReport } from '../../model/Defects';
import { Utils } from '../../utils/common/Utils';
import { Value } from 'arkanalyzer';
const defaultOptions: Options = {
    allowAny: true,
    allowBoolean: true,
    allowNullish: true,
    allowNumberAndString: true,
    allowRegExp: true,
    skipCompoundAssignments: false
};
interface Options {
    allowAny: boolean,
    allowBoolean: boolean,
    allowNullish: boolean,
    allowNumberAndString: boolean,
    allowRegExp: boolean,
    skipCompoundAssignments: boolean
};
type stmtType = {
    classSignature?: ClassSignature,
    methodSignature?: MethodSignature,
    realGenericTypes: string[]
};
type LeftOp = {
    constFlag: boolean,
    declaringStmt: Stmt,
    name: string,
    originalValue: string,
    type: stmtType,
    usedStmts: ArkAssignStmt[]
};
const logger = Logger.getLogger(LOG_MODULE_TYPE.HOMECHECK, 'RestrictPlusOperandsCheck');
const gmetaData: BaseMetaData = {
    severity: 2,
    ruleDocPath: 'docs/restrict-plus-operands-check.md', // TODO: support url
    description: 'Require both operands of addition to be the same type and be bigint, number, or string.'
};
export class RestrictPlusOperandsCheck implements BaseChecker {
    readonly metaData: BaseMetaData = gmetaData;
    public rule: Rule;
    public defects: Defects[] = [];
    public issues: IssueReport[] = [];
    private issueMap: Map<string, IssueReport> = new Map();
    private fileMatcher: FileMatcher = {
        matcherType: MatcherTypes.FILE,
    };
    private classMatcher: ClassMatcher = {
        file: [this.fileMatcher],
        matcherType: MatcherTypes.CLASS
    };
    private methodMatcher: MethodMatcher = {
        matcherType: MatcherTypes.METHOD,
        class: [this.classMatcher]
    };
    public registerMatchers(): MatcherCallback[] {
        const matchBuildCb: MatcherCallback = {
            matcher: this.methodMatcher,
            callback: this.check
        };
        return [matchBuildCb];
    };
    public check = (method: ArkMethod): void => {
        let declareClass = method.getDeclaringArkClass();
        const arkFile = declareClass.getDeclaringArkFile();
        // 将规则选项转换为Options类型
        let options: Options;
        if (this.rule && this.rule.option.length > 0) {
            options = this.rule.option[0] as Options;
        } else {
            options = defaultOptions;
        }
        // 初始化匹配名称数组和所有文本数组
        let matchNames: string[] = [];
        // 遍历方法体中的所有语句
        for (let stmt of method.getBody()?.getCfg().getStmts() ?? []) {
            this.processStatement(stmt, arkFile, options, matchNames);
        }
        this.reportSortedIssues();
    };

    private processStatement(stmt: Stmt, arkFile: ArkFile, options: Options, matchNames: string[]): void {
        // 获取语句的原始文本
        let text = stmt.getOriginalText() ?? '';
        if (!text) {
            return;
        }
        // 如果语句不是ArkAssignStmt类型的，则跳过
        if (!(stmt instanceof ArkAssignStmt)) {
            return;
        }
        // 获取赋值语句的右侧操作数
        let rightOp = stmt.getRightOp();
        if (!rightOp) {
            return;
        }
        // 如果文本匹配parseInt函数，并且右侧操作数不是二元操作符表达式或者操作符不是加号，则跳过
        if (!this.handleParseInt(stmt, arkFile, text, rightOp)) {
            return;
        }
        // 获取右侧操作数的类型
        let rightOpType = rightOp.getType();
        let rightOpTypeString = rightOpType.toString();
        // 如果右侧操作数类型为number，并且可以从文本中提取BigInt变量名
        if (rightOpTypeString === 'number' && this.extractBigIntVariableName(text)) {
            let matchName = this.extractBigIntVariableName(text);
            if (matchName) {
                matchNames.push(matchName);
            }
        }
        // 如果右侧操作数不是二元操作符表达式或者操作符不是加号，则跳过
        if (!(rightOp instanceof AbstractBinopExpr) || rightOp.getOperator() !== '+') {
            return;
        }
        this.handleBinaryOperation(stmt, arkFile, rightOp, options, matchNames);
    }

    private handleBinaryOperation(stmt: Stmt, arkFile: ArkFile, rightOp: AbstractBinopExpr, options: Options, matchNames: string[]): void {
        // 获取赋值语句的原始文本
        let textPlus = stmt.getOriginalText() ?? '';
        this.handleVariablePlus(stmt, arkFile, textPlus, rightOp, options);
        // 获取二元操作符表达式的两个操作数
        let op1 = rightOp.getOp1();
        let op2 = rightOp.getOp2();
        let op1TypeString = op1.getType().getTypeString();
        let op2TypeString = op2.getType().getTypeString();
        let op1Name = op1.toString();
        let op2Name = op2.toString();
        // 执行通用检查，如果检查结果为true，则添加问题报告
        this.handleGeneralInspection(stmt, arkFile, op1Name, op2Name, op1TypeString, op2TypeString, matchNames, textPlus, options);
    }

    private handleParseInt(stmt: Stmt, arkFile: ArkFile, text: string, rightOp: Value): boolean {
        if (this.matchParseInt(text)) {
            if (!(rightOp instanceof AbstractBinopExpr) || rightOp.getOperator() !== '+') {
                return false;
            }
            let op1 = rightOp.getOp1();
            let op2 = rightOp.getOp2();
            if (op1 !== op2) {
                return false;
            }
            this.addIssueReport(stmt, arkFile, text);
            return true;
        }
        return true;
    }

    private handleVariablePlus(stmt: Stmt, arkFile: ArkFile, text: string, rightOp: AbstractBinopExpr, options: Options): void {
        if (this.variablePlus(text)) {
            // 获取二元操作符表达式的两个操作数
            let op1 = rightOp.getOp1();
            let op1String = op1.toString();
            let op2 = rightOp.getOp2();
            let op2String = op2.toString();
            let op1TypeString = op1.getType().toString();
            let op2TypeString = op2.getType().toString();
            if (this.typeDifferent(op1TypeString, op2TypeString)) {
                this.addIssueReport(stmt, arkFile, text, options, op1TypeString, op2TypeString, op1String, op2String);
            }
        }
    }

    private handleGeneralInspection(stmt: Stmt, arkFile: ArkFile, op1Name: string, op2Name: string, op1TypeString: string,
        op2TypeString: string, matchNames: string[], textPlus: string, options: Options): void {
        if (this.generalInspection(op1Name, op2Name, op1TypeString, op2TypeString, matchNames, textPlus)) {
            if (this.bigIntPlusNumber(textPlus)) {
                op1TypeString = 'bigint'; // 修正这里为赋值操作
            }
            const regex = /\s*([^=,\s]+(?:\s+[^=,\s]+)*)/g;
            const match = textPlus.match(regex);
            if (match) {
                const afterEqual = match[1]?.trim();
                if (this.templateExpression(textPlus)) {
                    return; // 使用 return 替代 continue，因为这里不在循环中
                }
                if (afterEqual) {
                    this.addIssueReport(stmt, arkFile, afterEqual, options, op1TypeString, op2TypeString, op1Name, op2Name);
                }
            }
        }
    }

    private reportSortedIssues(): void {
        if (this.issueMap.size === 0) {
            return;
        }
        const sortedIssues = Array.from(this.issueMap.entries())
            .sort(([keyA], [keyB]) => Utils.sortByLineAndColumn(keyA, keyB));
        this.issues = [];
        sortedIssues.forEach(([_, issue]) => {
            RuleListUtil.push(issue.defect);
            this.issues.push(issue);
        });
    }
    /**
     * 判断给定的文本字符串中是否包含方括号 []
     *
     * @param text 要检查的文本字符串
     * @returns 如果文本中包含方括号，则返回 true；否则返回 false
     */
    private SquareBrackets(text: string): boolean {
        return /\[\]/g.test(text);
    }
    /**
     * 判断传入的字符串是否符合变量加法的正则表达式规则
     *
     * @param text 待判断的字符串
     * @returns 如果字符串符合变量加法的正则表达式规则，则返回 true，否则返回 false
     */
    private variablePlus(text: string): boolean {
        return /^[a-zA-Z_]\w*\s\+\s[a-zA-Z_]\w*$/.test(text);
    }
    /**
     * 检查文本中是否包含模板表达式
     *
     * @param text 要检查的文本
     * @returns 如果文本中包含模板表达式，则返回true，否则返回false
     */
    private templateExpression(text: string): boolean {
        return /\$\{.*?\}/.test(text);
    }

    private generalInspection(op1Name?: string, op2Name?: string, op1TypeString?: string,
        op2TypeString?: string, matchNames?: string[], textPlus?: string): boolean {
        const textPlusCheck = textPlus ?? '';
        const op1NameCheck = op1Name ?? '';
        const op2NameCheck = op2Name ?? '';
        const op1TypeStringCheck = op1TypeString ?? '';
        const op2TypeStringCheck = op2TypeString ?? '';
        const matchNamesCheck = matchNames ?? [];
        return this.bigIntPlusNumber(textPlusCheck) || this.typeDifferent(op1TypeStringCheck, op2TypeStringCheck) ||
            this.variableBigintPlusNumber(op1NameCheck, op2NameCheck, matchNamesCheck);
    }
    //检查两个操作数名称中是否有一个与给定的匹配名称数组中的某个名称相同，且另一个操作数名称与这个匹配名称不同。
    private variableBigintPlusNumber(op1Name: string, op2Name: string, matchNames: string[]): boolean {
        for (let matchName of matchNames) {
            if (op1Name === matchName) {
                if (op2Name !== matchName) {
                    return true;
                }
            } else if (op2Name === matchName) {
                if (op1Name !== matchName) {
                    return true;
                }
            }
        }
        return false;
    }
    //判断字符串是否表示一个有效的 bigInt 与数字的加法运算表达式
    private bigIntPlusNumber(textPlus: string,): boolean {
        if (this.bigintPlusNumberMatch(textPlus)) {
            return true;
        }
        return false;
    }
    //判断两个操作数类型字符串是否不同
    private typeDifferent(op1TypeString: string, op2TypeString: string): boolean {
        if (op1TypeString !== op2TypeString) {
            return true;
        }
        return false;
    }
    // 提取变量名，例如：const a = 1n; 或 let b = 2n; 等语句中的 'a' 和 'b'
    private extractBigIntVariableName(text: string): string | null {
        const regex = /\b(const|let|var)\s+(\w+)\s*=\s*\d+n\s*;\s*/;
        const match = text.match(regex);
        if (match) {
            // match[0] 是整个匹配的字符串
            // match[1] 是 const、let 或 var
            // match[2] 是变量名
            return match[2];
        } else {
            return null; // 没有找到匹配项
        }
    }
    // 检测是否存在 bigint + number 的情况，例如：a = 1n + 1; 或 a = 1 + 1n;
    private bigintPlusNumberMatch(text: string): boolean {
        return /=\s*\b(\d+n)\s*\+\s*(\d+)\b/.test(text);
    }
    //判断给定的文本中是否包含 parseInt 函数调用
    private matchParseInt(text: string): boolean {
        return /=\s*parseInt\s*\([^)]*\)/.test(text);
    }
    public checkCompoundAssignment(code: string): boolean {
        // 创建一个源代码文件对象
        const sourceFile = ts.createSourceFile('temp.ts', code, ts.ScriptTarget.Latest, true);
        let found: boolean = false;
        // 从源代码文件的根节点开始检查
        found = this.checkNode(sourceFile, found);
        // 返回是否找到了复合赋值符
        return found;
    };

    private checkNode(node: ts.Node, found: boolean): boolean {
        // 检查节点是否是二元表达式
        if (ts.isBinaryExpression(node)) {
            // 然后检查操作符是否为复合赋值符
            const operator = node.operatorToken.getText();
            if (['+=', '-=', '*=', '/=', '%=', '<<=', '>>=', '>>>=', '&=', '^=', '|='].includes(operator)) {
                found = true;
            }
        }
        // 递归检查节点的所有子节点
        ts.forEachChild(node, (child) => {
            found = this.checkNode(child, found);
        });
        return found;
    }
    private addIssueReport(stmt: Stmt, arkFile: ArkFile, name: string, options?: Options, op1TypeString?: string,
        op2TypeString?: string, op1String?: string, op2String?: string): void {
        const op1TypeStringCheck = op1TypeString ?? '';
        const op2TypeStringCheck = op2TypeString ?? '';
        const op1StringCheck = op1String ?? '';
        const op2StringCheck = op2String ?? '';
        const optionsCheck = options ?? defaultOptions;
        const severity = this.rule.alert ?? this.metaData.severity;
        const warnInfo = this.getLineAndColumn(stmt, arkFile, name,
            optionsCheck, op1TypeStringCheck, op2TypeStringCheck, op1StringCheck, op2StringCheck);
        if (warnInfo) {
            this.metaData.description = this.metaData.description.padEnd(130);
            const filePath = arkFile.getFilePath();
            let defects = new Defects(warnInfo.line, warnInfo.startCol, warnInfo.endCol, this.metaData.description, severity, this.rule.ruleId, filePath,
                this.metaData.ruleDocPath, true, false, false);
            RuleListUtil.push(defects);
            this.issues.push(new IssueReport(defects, undefined));
        }
    }
    private getLineAndColumn(stmt: Stmt, arkFile: ArkFile, name: string, options: Options, op1TypeString: string,
        op2TypeString: string, op1String: string, op2String: string): {
            line: number; startCol: number;
            endCol: number; filePath: string;
        } | null {
        if (!arkFile) {
            return null;
        }
        const code = arkFile.getCode() ?? '';
        const lines = code.split('\r\n');
        for (let i = 0; i < lines.length; i++) {
            const line = lines[i];
            const nextLine = lines[i + 1];
            let nameIndex = line.indexOf(name);
            if (nameIndex !== -1) {
                const originPosition = stmt.getOriginPositionInfo();
                const targetLine = originPosition.getLineNo();
                //精准找到错误的变量名所在的行列号
                // 选项都不是默认配置时
                const positionInfo = this.handleTypeCheckBasedOnOptions(
                    options, op1TypeString, op2TypeString, op1String, op2String,
                    line, targetLine, arkFile, nameIndex, stmt
                );
                if (positionInfo === null) {
                    continue;
                } else if (positionInfo !== undefined) {
                    return positionInfo;
                }
                //allowNullish为true时，允许null和undefined类型
                const positionInfoNode = this.handleTypeCheckBasedOnOptionsNode(
                    options, op1TypeString, op2TypeString, op1String, op2String, line, targetLine, arkFile, nameIndex, stmt
                );
                if (positionInfoNode === null) {
                    continue;
                } else if (positionInfoNode !== undefined) {
                    return positionInfoNode;
                }
                const result = this.handleOptionsBasedChecks(
                    options, stmt, line, op1String, op1TypeString, op2String, op2TypeString, targetLine, arkFile, nameIndex
                );
                if (result === null) {
                    continue;
                } else if (result !== undefined) {
                    return result;
                }
            }
        }
        return null;
    }

    private handleOptionsBasedChecks(
        options: Options, stmt: Stmt, line: string, op1String: string, op1TypeString: string,
        op2String: string, op2TypeString: string, targetLine: number, arkFile: ArkFile, nameIndex: number
    ): { line: number, startCol: number, endCol: number, filePath: string } | null | undefined {
        // 处理 skipCompoundAssignments 为 false 且其他选项都为 false 的情况
        if (!options.skipCompoundAssignments && !options.allowRegExp && !options.allowAny && !options.allowBoolean &&
            !options.allowNullish && !options.allowNumberAndString) {
            const positionInfo = this.handleCompoundAssignmentAndTypeCheck(
                stmt, line, op1String, op1TypeString, op2String, op2TypeString, targetLine, arkFile, nameIndex
            );
            return positionInfo ?? undefined;
        }

        // 处理所有选项都为默认配置的情况
        if (options.allowAny && options.allowBoolean && options.allowNullish &&
            options.allowNumberAndString && options.allowRegExp && !options.skipCompoundAssignments) {
            const positionInfo = this.handleCompoundAssignmentCheck(
                stmt, line, op1String, op1TypeString, op2String, targetLine, arkFile
            );
            if (positionInfo === null) {
                return null;
            } else if (positionInfo !== undefined) {
                return positionInfo;
            }
            return this.handleDefaultOptions(line, op1TypeString, op2TypeString, op1String, op2String, targetLine, arkFile, nameIndex);
        }
        return undefined;
    }

    private handleTypeCheckBasedOnOptionsNode(
        options: Options, op1TypeString: string, op2TypeString: string, op1String: string, op2String: string,
        line: string, targetLine: number, arkFile: ArkFile, nameIndex: number, stmt: Stmt
    ): { line: number, startCol: number, endCol: number, filePath: string } | null | undefined {
        return this.handleNullishCase(options)?.(
            op1TypeString, op2TypeString, op1String, op2String,
            line, targetLine, arkFile, nameIndex, stmt
        ) ?? this.handleNumberStringCase(options)?.(
            op1TypeString, op2TypeString, op1String, op2String,
            line, targetLine, arkFile, nameIndex, stmt
        ) ?? this.handleRegExpCase(options)?.(
            op1TypeString, op2TypeString, op1String, op2String,
            line, targetLine, arkFile, nameIndex, stmt
        );
    }

    // 处理 allowNullish 配置的情况
    private handleNullishCase(
        options: Options
    ): ((...args: Parameters<RestrictPlusOperandsCheck['handleTypeCheckAndPositionInfoNode2']>) =>
        ReturnType<RestrictPlusOperandsCheck['handleTypeCheckAndPositionInfoNode2']>
    ) | undefined {
        if (options.allowNullish &&
            !options.allowAny &&
            !options.allowBoolean &&
            !options.allowNumberAndString &&
            !options.allowRegExp &&
            options.skipCompoundAssignments) {
            return this.handleTypeCheckAndPositionInfoNode2.bind(this);
        }
        return undefined;
    }

    // 处理 allowNumberAndString 配置的情况
    private handleNumberStringCase(
        options: Options
    ): ((...args: Parameters<RestrictPlusOperandsCheck['handleTypeCheckAndPositionInfoNode1']>) =>
        ReturnType<RestrictPlusOperandsCheck['handleTypeCheckAndPositionInfoNode1']>
    ) | undefined {
        if (options.allowNumberAndString &&
            !options.allowAny &&
            !options.allowBoolean &&
            !options.allowNullish &&
            !options.allowRegExp &&
            options.skipCompoundAssignments) {
            return this.handleTypeCheckAndPositionInfoNode1.bind(this);
        }
        return undefined;
    }

    // 处理 allowRegExp 配置的情况
    private handleRegExpCase(
        options: Options
    ): ((...args: Parameters<RestrictPlusOperandsCheck['handleTypeCheckAndPositionInfoNode']>) =>
        ReturnType<RestrictPlusOperandsCheck['handleTypeCheckAndPositionInfoNode']>
    ) | undefined {
        if (options.allowRegExp &&
            !options.allowAny &&
            !options.allowBoolean &&
            !options.allowNullish &&
            !options.allowNumberAndString &&
            options.skipCompoundAssignments) {
            return this.handleTypeCheckAndPositionInfoNode.bind(this);
        }
        return undefined;
    }

    private handleTypeCheckBasedOnOptions(
        options: Options, op1TypeString: string, op2TypeString: string, op1String: string, op2String: string,
        line: string, targetLine: number, arkFile: ArkFile, nameIndex: number, stmt: Stmt
    ): { line: number, startCol: number, endCol: number, filePath: string } | null | undefined {
        return this.handleDefaultCase(options)?.(
            op1TypeString, op2TypeString, op1String, op2String,
            line, targetLine, arkFile, nameIndex, stmt
        ) ?? this.handleAnyCase(options)?.(
            op1TypeString, op2TypeString, op1String, op2String,
            line, targetLine, arkFile, nameIndex, stmt
        ) ?? this.handleBooleanCase(options)?.(
            op1TypeString, op2TypeString, op1String, op2String,
            line, targetLine, arkFile, nameIndex, stmt
        );
    }

    // 处理默认配置情况
    private handleDefaultCase(
        options: Options
    ): ((...args: Parameters<RestrictPlusOperandsCheck['handleTypeCheckAndPositionInfoNode5']>) =>
        ReturnType<RestrictPlusOperandsCheck['handleTypeCheckAndPositionInfoNode5']>
    ) | undefined {
        if (!options.allowAny &&
            !options.allowBoolean &&
            !options.allowNullish &&
            !options.allowNumberAndString &&
            !options.allowRegExp &&
            options.skipCompoundAssignments) {
            return this.handleTypeCheckAndPositionInfoNode5.bind(this);
        }
        return undefined;
    }

    // 处理 allowAny 配置的情况
    private handleAnyCase(
        options: Options
    ): ((...args: Parameters<RestrictPlusOperandsCheck['handleTypeCheckAndPositionInfoNode4']>) =>
        ReturnType<RestrictPlusOperandsCheck['handleTypeCheckAndPositionInfoNode4']>
    ) | undefined {
        if (options.allowAny &&
            !options.allowBoolean &&
            !options.allowNullish &&
            !options.allowNumberAndString &&
            !options.allowRegExp &&
            options.skipCompoundAssignments) {
            return this.handleTypeCheckAndPositionInfoNode4.bind(this);
        }
        return undefined;
    }

    // 处理 allowBoolean 配置的情况
    private handleBooleanCase(
        options: Options
    ): ((...args: Parameters<RestrictPlusOperandsCheck['handleTypeCheckAndPositionInfoNode3']>) =>
        ReturnType<RestrictPlusOperandsCheck['handleTypeCheckAndPositionInfoNode3']>
    ) | undefined {
        if (options.allowBoolean &&
            !options.allowAny &&
            !options.allowNullish &&
            !options.allowNumberAndString &&
            !options.allowRegExp &&
            options.skipCompoundAssignments) {
            return this.handleTypeCheckAndPositionInfoNode3.bind(this);
        }
        return undefined;
    }

    private handleCompoundAssignmentAndTypeCheck(
        stmt: Stmt, line: string, op1String: string, op1TypeString: string,
        op2String: string, op2TypeString: string, targetLine: number, arkFile: ArkFile, nameIndex: number
    ): { line: number, startCol: number, endCol: number, filePath: string } | null | undefined {
        const text = stmt.getOriginalText() ?? '';
        if (!text) {
            return null;
        }
        if (!(stmt instanceof ArkAssignStmt)) {
            return null;
        }
        if (this.checkCompoundAssignment(text)) {
            const positionInfo = this.handleCompoundAssignmentCheck(
                stmt, line, op1String, op1TypeString, op2String, targetLine, arkFile
            );
            return positionInfo ?? undefined;
        }
        return this.handleTypeCheckAndPositionInfo(
            op1TypeString, op2TypeString, op1String, op2String, line, targetLine, arkFile, nameIndex
        );
    }

    private handleTypeCheckAndPositionInfoNode5(
        op1TypeString: string, op2TypeString: string, op1String: string, op2String: string,
        line: string, targetLine: number, arkFile: ArkFile, nameIndex: number, stmt: Stmt
    ): { line: number, startCol: number, endCol: number, filePath: string } | null | undefined {
        // 检查 op1TypeString 是否为不允许的类型
        if (this.SquareBrackets(op1TypeString) ||
            this.isInvalidType(op1TypeString)) {
            const positionInfo = this.getPositionInfo(line, op1String, targetLine, arkFile);
            return positionInfo ?? undefined;
        }
        // 检查 op2TypeString 是否为不允许的类型
        else if (this.SquareBrackets(op2TypeString) ||
            this.isInvalidTypeNode(op2TypeString)) {
            const positionInfo = this.getPositionInfo(line, op2String, targetLine, arkFile);
            return positionInfo ?? undefined;
        }
        // 其他情况，返回位置信息
        else {
            return this.getPositionInfoByStmt(stmt, nameIndex, targetLine, arkFile);
        }
    }

    private handleTypeCheckAndPositionInfoNode4(
        op1TypeString: string, op2TypeString: string, op1String: string, op2String: string,
        line: string, targetLine: number, arkFile: ArkFile, nameIndex: number, stmt: Stmt
    ): { line: number, startCol: number, endCol: number, filePath: string } | null | undefined {
        // 检查 op1TypeString 是否为不允许的类型
        if (this.SquareBrackets(op1TypeString) ||
            this.isInvalidType1(op1TypeString)) {
            const positionInfo = this.getPositionInfo(line, op1String, targetLine, arkFile);
            return positionInfo ?? undefined;
        }
        // 检查 op2TypeString 是否为不允许的类型
        else if (this.SquareBrackets(op2TypeString) ||
            this.isInvalidTypeNode4(op2TypeString)) {
            const positionInfo = this.getPositionInfo(line, op2String, targetLine, arkFile);
            return positionInfo ?? undefined;
        }
        // 如果操作数类型为 any，跳过检查
        else if (op1TypeString === 'any' || op2TypeString === 'any') {
            return null;
        }
        // 其他情况，返回位置信息
        else {
            return this.getPositionInfoByStmt(stmt, nameIndex, targetLine, arkFile);
        }
    }

    private isInvalidType1(op1TypeString: string): boolean {
        return op1TypeString === 'never' || op1TypeString === 'UnknownKeyword' || op1TypeString === 'boolean' ||
            op1TypeString === 'null' || op1TypeString === 'undefined' || op1TypeString === 'RegExp';
    }

    private isInvalidTypeNode4(op2TypeString: string): boolean {
        return op2TypeString === 'never' || op2TypeString === 'UnknownKeyword' || op2TypeString === 'boolean' ||
            op2TypeString === 'null' || op2TypeString === 'undefined' || op2TypeString === 'RegExp';
    }

    private handleTypeCheckAndPositionInfoNode3(
        op1TypeString: string, op2TypeString: string, op1String: string, op2String: string,
        line: string, targetLine: number, arkFile: ArkFile, nameIndex: number, stmt: Stmt
    ): { line: number, startCol: number, endCol: number, filePath: string } | null | undefined {
        // 检查 op1TypeString 是否为不允许的类型
        if (this.SquareBrackets(op1TypeString) ||
            this.isInvalidType(op1TypeString)) {
            const op1Index = line.lastIndexOf(op1String);
            if (op1Index !== -1) {
                const startCol = op1Index + 1; // 列号从1开始
                const endCol = op1Index + 1;
                const originPath = arkFile.getFilePath();
                return { line: targetLine, startCol, endCol, filePath: originPath };
            }
            return undefined;
        }
        // 检查 op2TypeString 是否为不允许的类型
        else if (this.SquareBrackets(op2TypeString) ||
            this.isInvalidTypeNode1(op2TypeString)) {
            const positionInfo = this.getPositionInfo(line, op2String, targetLine, arkFile);
            return positionInfo ?? undefined;
        }
        // 如果操作数类型为 boolean，跳过检查
        else if (op1TypeString === 'boolean' || op2TypeString === 'boolean') {
            return null;
        }
        // 其他情况，返回位置信息
        else {
            return this.getPositionInfoByStmt(stmt, nameIndex, targetLine, arkFile);
        }
    }

    private isInvalidTypeNode1(op2TypeString: string): boolean {
        return op2TypeString === 'never' || op2TypeString === 'UnknownKeyword' || op2TypeString === 'any' ||
            op2TypeString === 'null' || op2TypeString === 'undefined' || op2TypeString === 'RegExp';
    }

    private handleTypeCheckAndPositionInfoNode2(
        op1TypeString: string, op2TypeString: string, op1String: string, op2String: string,
        line: string, targetLine: number, arkFile: ArkFile, nameIndex: number,
        stmt: Stmt
    ): { line: number, startCol: number, endCol: number, filePath: string } | null | undefined {
        // 检查 op1TypeString 是否为不允许的类型
        if (this.SquareBrackets(op1TypeString) ||
            op1TypeString === 'never' || op1TypeString === 'UnknownKeyword' || op1TypeString === 'any' || op1TypeString === 'boolean') {
            const positionInfo = this.getPositionInfo(line, op1String, targetLine, arkFile);
            return positionInfo ?? undefined;
        }
        // 检查 op2TypeString 是否为不允许的类型
        else if (this.SquareBrackets(op2TypeString) ||
            this.isInvalidTypeNode2(op2TypeString)) {
            const positionInfo = this.getPositionInfo(line, op2String, targetLine, arkFile);
            return positionInfo ?? undefined;
        }
        // 如果操作数类型为 null 或 undefined，跳过检查
        else if (op1TypeString === 'null' || op2TypeString === 'null' ||
            op1TypeString === 'undefined' || op2TypeString === 'undefined') {
            return null;
        }
        // 其他情况，返回位置信息
        else {
            return this.getPositionInfoByStmt(stmt, nameIndex, targetLine, arkFile);
        }
    }

    private isInvalidTypeNode2(op2TypeString: string): boolean {
        return op2TypeString === 'never' || op2TypeString === 'UnknownKeyword' || op2TypeString === 'any' ||
            op2TypeString === 'boolean' || op2TypeString === 'RegExp';
    }

    private handleTypeCheckAndPositionInfoNode1(
        op1TypeString: string, op2TypeString: string, op1String: string, op2String: string,
        line: string, targetLine: number, arkFile: ArkFile, nameIndex: number, stmt: Stmt
    ): { line: number, startCol: number, endCol: number, filePath: string } | null | undefined {
        return this.handleOp1TypeCheck(op1TypeString, op1String, line, targetLine, arkFile) ||
            this.handleOp2TypeCheck(op2TypeString, op2String, line, targetLine, arkFile) ||
            this.handleNumberStringCombination(op1TypeString, op2TypeString) ||
            this.handleStringNumberCase(op1TypeString, op2TypeString) ||
            this.getPositionInfoByStmt(stmt, nameIndex, targetLine, arkFile);
    }

    // 处理第一个操作数类型检查
    private handleOp1TypeCheck(
        op1Type: string,
        op1String: string,
        line: string,
        targetLine: number,
        arkFile: ArkFile
    ): { line: number, startCol: number, endCol: number, filePath: string } | null | undefined {
        if (this.SquareBrackets(op1Type) || this.isInvalidType(op1Type)) {
            return this.getPositionInfo(line, op1String, targetLine, arkFile);
        }
        return undefined;
    }

    // 处理第二个操作数类型检查
    private handleOp2TypeCheck(
        op2Type: string,
        op2String: string,
        line: string,
        targetLine: number,
        arkFile: ArkFile
    ): { line: number, startCol: number, endCol: number, filePath: string } | null | undefined {
        if (this.SquareBrackets(op2Type) || this.isInvalidTypeNode1(op2Type) || op2Type === 'RegExp') {
            return this.getPositionInfo(line, op2String, targetLine, arkFile);
        }
        return undefined;
    }

    // 处理 number|string 组合类型
    private handleNumberStringCombination(
        op1Type: string,
        op2Type: string
    ): null | undefined {
        if (['number|string', 'string|number'].includes(op1Type)) {
            if (['string', 'number'].includes(op2Type)) { return null };
        }
        if (['number|string', 'string|number'].includes(op2Type)) {
            if (['string', 'number'].includes(op1Type)) { return null };
        }
        return undefined;
    }

    // 处理 string 和 number 互操作情况
    private handleStringNumberCase(
        op1Type: string,
        op2Type: string
    ): null | undefined {
        if ((op1Type === 'string' && op2Type === 'number') ||
            (op1Type === 'number' && op2Type === 'string')) {
            return null;
        }
        return undefined;
    }

    private handleTypeCheckAndPositionInfoNode(
        op1TypeString: string, op2TypeString: string, op1String: string, op2String: string, line: string,
        targetLine: number, arkFile: ArkFile, nameIndex: number, stmt: Stmt
    ): { line: number, startCol: number, endCol: number, filePath: string } | null | undefined {
        if (this.SquareBrackets(op1TypeString) ||
            op1TypeString === 'never' || op1TypeString === 'UnknownKeyword' || op1TypeString === 'any' ||
            op1TypeString === 'boolean' || op1TypeString === 'null' || op1TypeString === 'undefined') {
            const positionInfo = this.getPositionInfo(line, op1String, targetLine, arkFile);
            return positionInfo ?? undefined;
        } else if (this.SquareBrackets(op2TypeString) ||
            this.isInvalidTypeNode3(op2TypeString)) {
            const positionInfo = this.getPositionInfo(line, op2String, targetLine, arkFile);
            return positionInfo ?? undefined;
        } else if (op1TypeString === 'string' && op2TypeString === 'RegExp') {
            return null;
        } else if (op2TypeString === 'string' && op1TypeString === 'RegExp') {
            return null;
        } else {
            return this.getPositionInfoByStmt(stmt, nameIndex, targetLine, arkFile);
        }
    }

    private isInvalidTypeNode3(op2TypeString: string): boolean {
        return op2TypeString === 'never' || op2TypeString === 'UnknownKeyword' || op2TypeString === 'any' ||
            op2TypeString === 'boolean' || op2TypeString === 'RegExp';
    }

    private getPositionInfoByStmt(
        stmt: Stmt,
        nameIndex: number,
        targetLine: number,
        arkFile: ArkFile
    ): { line: number, startCol: number, endCol: number, filePath: string } | null {
        const text = stmt.getOriginalText() ?? '';
        if (!text) {
            return null;
        }
        if (!(stmt instanceof ArkAssignStmt)) {
            return null;
        }
        if (this.checkCompoundAssignment(text)) {
            return null;
        }
        const startCol = nameIndex + 1; // 列号从1开始
        const endCol = nameIndex + 1;
        const originPath = arkFile.getFilePath();
        return { line: targetLine, startCol, endCol, filePath: originPath };
    }

    private handleTypeCheckAndPositionInfo(
        op1TypeString: string, op2TypeString: string, op1String: string, op2String: string,
        line: string, targetLine: number, arkFile: ArkFile, nameIndex: number
    ): { line: number, startCol: number, endCol: number, filePath: string } | null {
        if (this.SquareBrackets(op1TypeString) ||
            this.isInvalidType(op1TypeString)) {
            const positionInfo = this.getPositionInfo(line, op1String, targetLine, arkFile);
            if (positionInfo) {
                return positionInfo;
            }
        } else if (this.SquareBrackets(op2TypeString) ||
            this.isInvalidTypeNode(op2TypeString)) {
            const positionInfo = this.getPositionInfo(line, op2String, targetLine, arkFile);
            if (positionInfo) {
                return positionInfo;
            }
        } else {
            const startCol = nameIndex + 1; // 列号从1开始
            const endCol = nameIndex + 1;
            const originPath = arkFile.getFilePath();
            return { line: targetLine, startCol, endCol, filePath: originPath };
        }
        return null;
    }

    private isInvalidTypeNode(op2TypeString: string): boolean {
        return op2TypeString === 'never' || op2TypeString === 'UnknownKeyword' || op2TypeString === 'any' || op2TypeString === 'boolean' ||
            op2TypeString === 'null' || op2TypeString === 'undefined' || op2TypeString === 'RegExp';
    }

    private isInvalidType(op1TypeString: string): boolean {
        return op1TypeString === 'never' || op1TypeString === 'UnknownKeyword' || op1TypeString === 'any' || op1TypeString === 'boolean' ||
            op1TypeString === 'null' || op1TypeString === 'undefined' || op1TypeString === 'RegExp';
    }

    private handleCompoundAssignmentCheck(
        stmt: Stmt, line: string, op1String: string,
        op1TypeString: string, op2String: string, targetLine: number, arkFile: ArkFile
    ): { line: number, startCol: number, endCol: number, filePath: string } | null | undefined {
        if (!(stmt instanceof ArkAssignStmt)) {
            return null;
        }
        const leftOp = stmt.getLeftOp();
        const leftOpName = leftOp.toString();
        const rightOp = stmt.getRightOp();
        if (leftOpName === op1String) {
            if (!(rightOp instanceof AbstractBinopExpr)) {
                return null;
            }
            const op2 = rightOp.getOp2();
            const op2Type = op2.getType().toString();
            if (op1TypeString !== op2Type) {
                return this.getPositionInfo(line, op2String, targetLine, arkFile);
            }
            return null;
        }
        return undefined;
    }

    private handleDefaultOptions(line: string, op1TypeString: string, op2TypeString: string, op1String: string, op2String: string,
        targetLine: number, arkFile: ArkFile, nameIndex: number): {
            line: number, startCol: number, endCol: number, filePath: string
        } | null {
        if (this.SquareBrackets(op1TypeString) || op1TypeString === 'never' || op1TypeString === 'UnknownKeyword') {
            const positionInfo = this.getPositionInfo(line, op1String, targetLine, arkFile);
            if (positionInfo) {
                return positionInfo;
            }
        } else if (this.SquareBrackets(op2TypeString) || op2TypeString === 'never' || op2TypeString === 'UnknownKeyword') {
            const positionInfo = this.getPositionInfo(line, op2String, targetLine, arkFile);
            if (positionInfo) {
                return positionInfo;
            }
        } else if (op1TypeString === 'number' && op2TypeString === 'RegExp') {
            const positionInfo = this.getPositionInfo(line, op2String, targetLine, arkFile);
            if (positionInfo) {
                return positionInfo;
            }
        } else if (op1TypeString === 'RegExp' && op2TypeString === 'number') {
            const positionInfo = this.getPositionInfo(line, op2String, targetLine, arkFile);
            if (positionInfo) {
                return positionInfo;
            }
        } else if (this.isTypeAllowed(op1TypeString, op2TypeString)) {
            return null;
        } else {
            const startCol = nameIndex + 1; // 列号从1开始
            const endCol = nameIndex + 1;
            const originPath = arkFile.getFilePath();
            return { line: targetLine, startCol, endCol, filePath: originPath };
        }
        return null;
    }

    private isTypeAllowed(op1TypeString: string, op2TypeString: string): boolean {
        return op1TypeString === 'any' || op2TypeString === 'any' ||
            op1TypeString === 'boolean' || op2TypeString === 'boolean' ||
            (op1TypeString === 'null' || op2TypeString === 'null' || op1TypeString === 'undefined' || op2TypeString === 'undefined') ||
            (op1TypeString === 'number|string' || op1TypeString === 'string|number' && (op2TypeString === 'string' || op2TypeString === 'number')) ||
            (op2TypeString === 'number|string' || op2TypeString === 'string|number' && (op1TypeString === 'string' || op1TypeString === 'number')) ||
            (op1TypeString === 'string' && op2TypeString === 'number') ||
            (op1TypeString === 'number' && op2TypeString === 'string') ||
            (op1TypeString === 'string' && op2TypeString === 'RegExp') ||
            (op2TypeString === 'string' && op1TypeString === 'RegExp');
    }

    private getPositionInfo(line: string, targetString: string, targetLine: number, arkFile: ArkFile): {
        line: number, startCol: number, endCol: number, filePath: string
    } | null {
        const targetIndex = line.lastIndexOf(targetString);
        if (targetIndex !== -1) {
            const startCol = targetIndex + 1; // 列号从1开始
            const endCol = targetIndex + 1;
            const originPath = arkFile.getFilePath();
            return { line: targetLine, startCol, endCol, filePath: originPath };
        }
        return null;
    }
}